#region Copyright (c) 2003, newtelligence AG. All rights reserved.
/*
// Copyright (c) 2003, newtelligence AG. (http://www.newtelligence.com)
// Original BlogX Source Code: Copyright (c) 2003, Chris Anderson (http://simplegeek.com)
// All rights reserved.
//  
// Redistribution and use in source and binary forms, with or without modification, are permitted 
// provided that the following conditions are met: 
//  
// (1) Redistributions of source code must retain the above copyright notice, this list of 
// conditions and the following disclaimer. 
// (2) Redistributions in binary form must reproduce the above copyright notice, this list of 
// conditions and the following disclaimer in the documentation and/or other materials 
// provided with the distribution. 
// (3) Neither the name of the newtelligence AG nor the names of its contributors may be used 
// to endorse or promote products derived from this software without specific prior 
// written permission.
//      
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// -------------------------------------------------------------------------
//
// Original BlogX source code (c) 2003 by Chris Anderson (http://simplegeek.com)
// 
// newtelligence is a registered trademark of newtelligence Aktiengesellschaft.
// 
// For portions of this software, the some additional copyright notices may apply 
// which can either be found in the license.txt file included in the source distribution
// or following this notice. 
//
*/
#endregion

namespace newtelligence.DasBlog.Web.Services
{
    using System;
    using System.Configuration;
    using System.Collections;
	using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Web.Services.Description;
    using System.IO;
    using System.Xml;
    using System.Xml.Serialization;
    using newtelligence.DasBlog.Runtime;
    using newtelligence.DasBlog.Web.Services.Rss20;
    using newtelligence.DasBlog.Web.Core;
    using newtelligence.DasBlog.Util.Html;

    /// <summary>
    /// Summary description for DasBlogBrowsing.
    /// </summary>
    [WebService(Namespace="urn:schemas-newtelligence-com:dasblog:syndication-services")]
    public class SyndicationServiceImplementation : SyndicationServiceBase 
    {
        protected bool inASMX = false;

        public SyndicationServiceImplementation()
        {
            InitializeComponent();
        }

        public SyndicationServiceImplementation(SiteConfig siteConfig, IBlogDataService dataService, ILoggingDataService loggingService):
           base( siteConfig, dataService, loggingService )
        {
            //CODEGEN: This call is required by the ASP.NET Web Services Designer
            InitializeComponent();
        }


        #region Component Designer generated code
		
        //Required by the Web Services Designer 
        private IContainer components = null;
				
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if(disposing && components != null)
            {
                components.Dispose();
            }
            base.Dispose(disposing);		
        }
		
        #endregion

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public string GetRssUrl()
        {
            return Utils.GetRssUrl(siteConfig);
        }

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public string GetCommentsRssUrl() 
        {
            return Utils.GetCommentsRssUrl(siteConfig);
        }
		
		// OmarS: no longer need CacheDuration since we are sending Last Modified
		// and ETag
        //[WebMethod(CacheDuration=60)]
        [WebMethod]
		[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        [return:XmlAnyElement]
        public RssRoot GetRss()
        {
            int maxDays = siteConfig.RssDayCount;
			int maxEntries = siteConfig.RssMainEntryCount;
			return GetRssWithCounts(maxDays, maxEntries);
        }

        [WebMethod(CacheDuration=180)]
		[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        [return:XmlAnyElement]
        public RssRoot GetCommentsRss() 
        {
            CommentCollection _com = dataService.GetAllComments();
            _com = ReorderComments(_com);
            return GetCommentsRssCore(_com);
        }

        //[WebMethod(CacheDuration=180)]
        [WebMethod]
		[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        [return:XmlAnyElement]
        public RssRoot GetEntryCommentsRss(string guid) 
        {
            CommentCollection _com = dataService.GetCommentsFor(guid);
            _com = ReorderComments(_com);
            return GetCommentsRssCore(_com);
        }

        [WebMethod(CacheDuration=60)]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        [return:XmlAnyElement]
        public RssRoot GetRssCategory(string categoryName)
        {
            return GetRssCore(categoryName, 0, 0);
        }

        [WebMethod(CacheDuration=60)]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        [return:XmlAnyElement]
        public RssRoot GetRssWithCounts(int maxDayCount, int maxEntryCount)
        {
            return GetRssCore(null, maxDayCount, maxEntryCount);
        }

        private RssRoot GetRssCore(string category, int maxDayCount, int maxEntryCount)
        {
			EntryCollection entries = null;
			//We only build the entries if blogcore doesn't exist and we'll need them later...
			if (dataService.GetLastEntryUpdate() == DateTime.MinValue)
			{
				entries = BuildEntries(category, maxDayCount, maxEntryCount);
			}
			
			//Try to get out as soon as possible with as little CPU as possible
			if ( inASMX )
			{
				DateTime lastModified = GetLatestModifedEntryDateTime(entries);
				if (GetStatusNotModified(lastModified))
					return null;
			}

			if ( inASMX )
			{
				string referrer = Context.Request.UrlReferrer!=null?Context.Request.UrlReferrer.AbsoluteUri:"";
				if (ReferralBlackList.IsBlockedReferrer(referrer))
				{
					if (siteConfig.EnableReferralUrlBlackList404s)
					{
						return null;
					}
				}
				else
				{
					loggingService.AddReferral(
						new LogDataItem(
						Context.Request.RawUrl,
						referrer,
						Context.Request.UserAgent));
				}
			}

			//not-modified didn't work, do we have this in cache?
			string CacheKey = "Rss:" + category + ":" + maxDayCount.ToString() + ":" + maxEntryCount.ToString();
			RssRoot documentRoot = HttpContext.Current.Cache[CacheKey] as RssRoot;
			if (documentRoot == null) //we'll have to build it...
			{
				//However, if we made it this far, the not-modified check didn't work, and we may not have entries...
				if(entries == null)
				{
					entries = BuildEntries(category, maxDayCount, maxEntryCount);			
				}

				documentRoot = new RssRoot();
				documentRoot.Namespaces.Add("trackback","http://madskills.com/public/xml/rss/module/trackback/");
				documentRoot.Namespaces.Add("pingback","http://madskills.com/public/xml/rss/module/pingback/");
				if (siteConfig.EnableComments) 
				{
					documentRoot.Namespaces.Add("wfw","http://wellformedweb.org/CommentAPI/");
					documentRoot.Namespaces.Add("slash","http://purl.org/rss/1.0/modules/slash/");
				}
				RssChannel ch = new RssChannel();
            
				ch.Title = siteConfig.Title;
				if ( siteConfig.Description == null || siteConfig.Description.Trim().Length == 0 )
				{
					ch.Description = siteConfig.Subtitle;
				}
				else
				{
					ch.Description=siteConfig.Description;
				}
            
				ch.Link = Utils.GetBaseUrl(siteConfig);
				ch.Copyright = siteConfig.Copyright;
				ch.ManagingEditor = siteConfig.Contact;
				ch.WebMaster = siteConfig.Contact;
				ch.Image = null;
				if (siteConfig.ChannelImageUrl != null && siteConfig.ChannelImageUrl.Trim().Length > 0)
				{
					ChannelImage channelImage = new ChannelImage();
					channelImage.Title = ch.Title;
					channelImage.Link = ch.Link;
					if (siteConfig.ChannelImageUrl.StartsWith("http"))
					{
						channelImage.Url = siteConfig.ChannelImageUrl;
					}
					else
					{
						channelImage.Url = Utils.RelativeToRoot(siteConfig,siteConfig.ChannelImageUrl);
					}
					ch.Image = channelImage;
				}

				documentRoot.Channels.Add(ch);

				foreach (Entry entry in entries)
				{
					if (entry.IsPublic == false)
					{
						continue;
					}
					XmlDocument doc2 = new XmlDocument();
					ArrayList anyElements = new ArrayList();
					RssItem item = new RssItem();
					item.Title = entry.Title;
					item.Guid = Utils.GetPermaLinkUrl(siteConfig, entry.EntryId);
					item.Link = Utils.GetPermaLinkUrl(siteConfig, entry);

					XmlElement trackbackPing = doc2.CreateElement("trackback","ping","http://madskills.com/public/xml/rss/module/trackback/");
					trackbackPing.InnerText= Utils.GetTrackbackUrl(siteConfig,entry.EntryId);
					anyElements.Add(trackbackPing);
					XmlElement pingbackServer = doc2.CreateElement("pingback","server","http://madskills.com/public/xml/rss/module/pingback/");
					pingbackServer.InnerText= new Uri(new Uri(Utils.GetBaseUrl(siteConfig)), "pingback.aspx").ToString();
					anyElements.Add(pingbackServer);
					XmlElement pingbackTarget = doc2.CreateElement("pingback","target","http://madskills.com/public/xml/rss/module/pingback/");
					pingbackTarget.InnerText=Utils.GetPermaLinkUrl(siteConfig, entry.EntryId);
					anyElements.Add(pingbackTarget);
					if (siteConfig.EnableComments) 
					{
						if (entry.AllowComments)
						{
							XmlElement commentApi = doc2.CreateElement("wfw","comment","http://wellformedweb.org/CommentAPI/");
							commentApi.InnerText=Utils.GetCommentViewUrl(siteConfig,entry.EntryId);
							anyElements.Add(commentApi);
						}

						XmlElement commentRss = doc2.CreateElement("wfw","commentRss","http://wellformedweb.org/CommentAPI/");
						commentRss.InnerText=Utils.GetEntryCommentsRssUrl(siteConfig,entry.EntryId);
						anyElements.Add(commentRss);
						XmlElement slashComments = doc2.CreateElement("slash","comments","http://purl.org/rss/1.0/modules/slash/");
						slashComments.InnerText=dataService.GetCommentsFor(entry.EntryId).Count.ToString();
						anyElements.Add(slashComments);
						item.Comments = Utils.GetCommentViewUrl(siteConfig,entry.EntryId);
					}
					item.Language = entry.Language;
					if (entry.Categories != null && entry.Categories.Length > 0)
					{
						item.Category = entry.GetSplitCategories()[0];
					}
					if (entry.Attachments.Count > 0)
					{
						// RSS currently supports only a single enclsoure so we return the first one	
						item.Enclosure =  new Enclosure();
						item.Enclosure.Url = Utils.GetEnclosureLinkUrl(entry.EntryId, entry.Attachments[0]);
						item.Enclosure.Type = entry.Attachments[0].Type;
						item.Enclosure.Length = entry.Attachments[0].Length.ToString();
					}
					item.PubDate = entry.CreatedUtc.ToString("R");
					if (ch.LastBuildDate == null || ch.LastBuildDate.Length == 0)
					{
						ch.LastBuildDate = item.PubDate;
					}


					if (!siteConfig.AlwaysIncludeContentInRSS && 
						entry.Description != null && 
						entry.Description.Trim().Length > 0)
					{
						item.Description = PreprocessItemContent(entry.EntryId, entry.Description);
                    
					}
					else
					{
						item.Description = ContentFormatter.FormatContentAsHTML(PreprocessItemContent(entry.EntryId, entry.Content));
						try
						{
							doc2.LoadXml(ContentFormatter.FormatContentAsXHTML(PreprocessItemContent(entry.EntryId, entry.Content)));
							anyElements.Add( (XmlElement)doc2.SelectSingleNode("//*[local-name() = 'body'][namespace-uri()='http://www.w3.org/1999/xhtml']") );
						}
						catch 
						{
							// absorb
						}
					}

					item.anyElements = (XmlElement[])anyElements.ToArray(typeof(XmlElement));
					ch.Items.Add(item);
				}
				HttpContext.Current.Cache.Insert(CacheKey,documentRoot,null,DateTime.Now.AddMinutes(5),System.Web.Caching.Cache.NoSlidingExpiration);
			}
            return documentRoot;
        }

		
        private CommentCollection ReorderComments(CommentCollection _comments) 
        {
            CommentCollection _tempCol = new CommentCollection();
            for (int i = 0 ; i < _comments.Count ; i++) 
            {
                _tempCol.Add(_comments[_comments.Count - (i+1)]);
            }
            return _tempCol;
        }
		
        private RssRoot GetCommentsRssCore(CommentCollection _com) 
        {
			//Try to get out as soon as possible with as little CPU as possible
			if ( inASMX )
			{
				DateTime lastModified = GetLatestModifedCommentDateTime(_com);
				if (GetStatusNotModified(lastModified))
					return null;
			}

            if ( inASMX )
            {
				string referrer = Context.Request.UrlReferrer!=null?Context.Request.UrlReferrer.AbsoluteUri:"";
				if (ReferralBlackList.IsBlockedReferrer(referrer) == false)
				{
					loggingService.AddReferral(
						new LogDataItem(
						Context.Request.RawUrl,
						referrer,
						Context.Request.UserAgent));
				}            
			}
			
			// TODO: Figure out why this code is copied and pasted from above rather than using a function (shame!)
            RssRoot documentRoot = new RssRoot();
            RssChannel ch = new RssChannel();
            ch.Title = siteConfig.Title + " - Comments";
            ch.Link = Utils.GetBaseUrl(siteConfig);
            ch.Copyright = siteConfig.Copyright;
            ch.ManagingEditor = siteConfig.Contact;
            ch.WebMaster = siteConfig.Contact;
			documentRoot.Channels.Add(ch);
			
            int i = 0;
            foreach (Comment c in _com) 
            {
                ArrayList anyElements = new ArrayList();
                if (i == siteConfig.RssEntryCount) { break; }
                i++;
                string tempTitle = "";
                RssItem item = new RssItem();

                if(c.TargetTitle != null && c.TargetTitle.Length > 0 ) 
                {
                    tempTitle = " on \"" + c.TargetTitle + "\"";
                }

                if(c.Author == null || c.Author == "") 
                {
                    item.Title = "Comment" + tempTitle;
                }
                else 
                {
                    item.Title = "Comment by " + c.Author + tempTitle;
                }

				item.Guid = item.Link = Utils.GetPermaLinkUrl(siteConfig, c.EntryId);
 
				item.PubDate = c.CreatedUtc.ToString("R");
				
                if (c.AuthorHomepage == null || c.AuthorHomepage == "") 
                {
                    if (c.AuthorEmail == null || c.AuthorEmail == "") 
                    {
                        if(c.Author == null || c.Author == "") 
                        {
                            item.Description = c.Content;
                        }
                        else 
                        {
                            item.Description = c.Content + "<br /><br />" + "Posted by: " + c.Author;
                        }
                    }
                    else 
                    {
						string content = c.Content.Replace(Environment.NewLine, "<br />");
                        item.Description = content + "<br /><br />" + "Posted by: " + "<a href=\"mailto:" + c.AuthorEmail +
                            "\">" + c.Author + "</a>";
                    }
                }
                else 
                {
                    
                    if (c.AuthorHomepage.IndexOf("http://") < 0) 
                    {
                        c.AuthorHomepage = "http://" + c.AuthorHomepage; 
                    }
                    item.Description = c.Content + "<br /><br />" + "Posted by: " + "<a href=\"" + c.AuthorHomepage +
                        "\">" + c.Author + "</a>";
                }

                if (c.Author != null && c.Author.Length > 0 ) 
                {
                    item.Author = c.AuthorEmail + " (" + c.Author + ")";
                }

                item.Comments = Utils.GetCommentViewUrl(siteConfig,c.TargetEntryId);
				
                if (ch.LastBuildDate == null || ch.LastBuildDate.Length == 0) 
                {
                    ch.LastBuildDate = item.PubDate;
                }
					
                XmlDocument doc2 = new XmlDocument();
                try 
                {
                    doc2.LoadXml(c.Content);
                    anyElements.Add( (XmlElement)doc2.SelectSingleNode("//*[local-name() = 'body'][namespace-uri()='http://www.w3.org/1999/xhtml']") );
                }
                catch 
                {
                    // absorb
                }
                item.anyElements = (XmlElement[])anyElements.ToArray(typeof(XmlElement));
                ch.Items.Add(item);
            }


            return documentRoot;
        }

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public LogDataItemCollection GetReferrerLog(DateTime date)
        {
            return loggingService.GetReferralsForDay(date);
        }

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public string[] GetCategoryList()
        {
            CategoryCacheEntryCollection categories = dataService.GetCategories();
            string[] cats = new string[categories.Count];
            for (int i=0; i<cats.Length; i++)
            {
                cats[i] = categories[i].Name;
            }

            return cats;
        }

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public EntryCollection GetCategoryEntries(string categoryName)
        {
            return dataService.GetEntriesForCategory( categoryName, null );
        }

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public DateTime[] GetDaysWithEntries()
        {
            return dataService.GetDaysWithEntries(new newtelligence.DasBlog.Util.UTCTimeZone());
        }

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public DayEntry GetDayEntry(DateTime date)
        {
            return dataService.GetDayEntry(date);
        }

        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public DayExtra GetDayExtra(DateTime date)
        {
            return dataService.GetDayExtra(date);
        }
		
		//OmarS: Why is this here? i see no good reason for this
		/*
        [WebMethod]
        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Wrapped,Use=SoapBindingUse.Literal)]
        public void AddComment(Comment comment)
        {
            dataService.AddComment( comment );
        }
		*/
    }
}
